﻿/* ######################################## *\
 * ### Copyright (C) 2009 AJ Ravindiran ### *
\* ######################################## */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using AJRavindiran.Jolt.Utilities;
using AJRavindiran.Jolt.RuneScape.Database;
using AJRavindiran.Jolt.RuneScape.Models.Characters;
using AJRavindiran.Jolt.RuneScape.Models.Characters.Information;

namespace AJRavindiran.Jolt.RuneScape.Network.Login
{
    /// <summary>
    /// Represents a login server.
    /// </summary>
    public class LoginServer
    {
        #region Fields
        /// <summary>
        /// Container for all queued characters.
        /// </summary>
        private HashSet<Character> queuedCharacters = new HashSet<Character>();

        private LoginUpdateServer loginUpdateServer;
        private LoginStreamBuffer loginStreamBuffer;
        #endregion Fields

        #region Constructors
        /// <summary>
        /// Constructs a new login server class.
        /// </summary>
        public LoginServer()
        {
            Jolt.GetLog().WriteInfo("Constructing login server...");

            // Initialize sister clases.
            this.loginUpdateServer = new LoginUpdateServer();
            this.loginStreamBuffer = new LoginStreamBuffer();
        }
        #endregion Constructors

        #region Methods
        /// <summary>
        /// Attempts to login all the users queued in the container.
        /// </summary>
        /// <param name="character"></param>
        public void Process(Character character)
        {
            try
            {
                character.GetDetails().ServerSessionKey = ((long)(new Random().NextDouble() * 99999999D) << 32) + (long)(new Random().NextDouble() * 99999999D);
                character.GetDetails().ClientSessionKey = 0;
                LoginCodesEnumeration returnCode = LoginCodesEnumeration.LoginOK;

                if (character.GetStatus().LoginStage < -1)
                {
                    this.loginUpdateServer.SendUpdateKeys(character);
                }
                else if (character.GetStatus().LoginStage == 0)
                {
                    try
                    {
                        if (!this.loginStreamBuffer.FillStream(character, 2))
                            return;
                    }
                    catch (Exception) { return; }

                    byte connectionType = character.GetStreamReader().ReadByte();

                    if (connectionType == 15)
                    {
                        this.loginUpdateServer.SendUpdateKeys(character);
                        character.GetStatus().LoginStage = -5;
                        return;
                    }

                    if (connectionType != 14)
                    {
                        character.GetStatus().LoginStage = -1;
                        return;
                    }

                    byte longPlayerName = character.GetStreamReader().ReadByte();
                    character.GetStreamWriter().WriteByte(0);
                    character.GetStreamWriter().WriteLong(character.GetDetails().ServerSessionKey);
                    this.loginStreamBuffer.DirectFlushStream(character);
                    character.GetStatus().LoginStage++;
                }
                else if (character.GetStatus().LoginStage == 1)
                {
                    try
                    {
                        if (!this.loginStreamBuffer.FillStream(character, 3))
                            return;
                    }
                    catch (Exception) { return; }

                    int loginType = character.GetStreamReader().ReadByte();
                    if (loginType != 16 && loginType != 18 && loginType != 14)
                    {
                        character.GetStatus().LoginStage = -1;
                        return;
                    }

                    character.GetStatus().LoginStage++;
                }
                else if (character.GetStatus().LoginStage == 2)
                {
                    int loginPacketSize = character.GetStreamReader().ReadUShort();
                    int loginEncryptPacketSize = loginPacketSize - (36 + 1 + 1 + 2);

                    if (loginEncryptPacketSize <= 0)
                    {
                        character.GetStatus().LoginStage = -1;
                        return;
                    }

                    try
                    {
                        if (!this.loginStreamBuffer.FillStream(character, loginPacketSize))
                            return;
                    }
                    catch (Exception ex)
                    {
                        Jolt.GetLog().WriteException(ex);
                        return;
                    }

                    int clientVersion = character.GetStreamReader().ReadInt();
                    if (clientVersion != 508)
                    {
                        character.GetStatus().LoginStage = -1;
                        return;
                    }

                    character.GetStreamReader().ReadByte();
                    character.GetStreamReader().ReadUShort();
                    character.GetStreamReader().ReadUShort();
                    for (int i = 0; i < 24; i++)
                    {
                        int cacheIndex = character.GetStreamReader().ReadByte();
                    }

                    string junk = character.GetStreamReader().ReadString();
                    for (int i = 0; i < 29; i++)
                    {
                        int junk2 = character.GetStreamReader().ReadInt();
                    }

                    loginEncryptPacketSize--;
                    byte junk29 = character.GetStreamReader().ReadByte();
                    byte encryption = character.GetStreamReader().ReadByte();
                    if (encryption != 10 & encryption != 64)
                    {
                        character.GetStatus().LoginStage = -1;
                        return;
                    }

                    character.GetDetails().ClientSessionKey = character.GetStreamReader().ReadLong();
                    character.GetDetails().ServerSessionKey = character.GetStreamReader().ReadLong();
                    character.GetDetails().Username = StringUtilities.FormatForDisplay(MathUtilities.LongToString(character.GetStreamReader().ReadLong()).ToLower().Trim());
                    if (character.GetDetails().Username == null)
                    {
                        character.GetStatus().LoginStage = -1;
                        character.GetDetails().Username = "null";
                        return;
                    }

                    for (int i = 0; i < character.GetDetails().Username.Length; i++)
                    {
                        char c = character.GetDetails().Username[i];
                        if (!char.IsLetterOrDigit(c) && !char.IsWhiteSpace(c))
                        {
                            character.GetStatus().LoginStage = -1;
                            character.GetDetails().Username = "null";
                            return;
                        }
                    }

                    OffenseTypes offense = RuneScape.GetOffenceManager().GetOffenseType(character);
                    if (RuneScape.GetCharacterManager().IsOnline(character.GetDetails().Username, character))
                        returnCode = LoginCodesEnumeration.AlreadyOnline;
                    if (offense.Equals(OffenseTypes.Ban))
                        returnCode = LoginCodesEnumeration.AccountDisabled;
                    if (offense.Equals(OffenseTypes.IPBan))
                        returnCode = LoginCodesEnumeration.AccountDisabled;
                    if (offense.Equals(OffenseTypes.Mute))
                        character.GetStatus().Muted = true;
                    else
                        character.GetStatus().Muted = false;

                    string password = character.GetStreamReader().ReadString();
                    if (password == null)
                    {
                        character.GetStatus().LoginStage = -1;
                        return;
                    }
                    bool validLoad = RuneScape.GetAccountManager().LoadCharacter(character);

                    try
                    {
                        //password = Utilities.Security.Cryptography.MD5[password];
                    }
                    catch (Exception ex)
                    {
                        Jolt.GetLog().WriteException(ex);
                        password = "null";
                    }

                    if (password != null && character.GetDetails().Password != null
                        && character.GetDetails().Password != "null"
                        && !character.GetDetails().Password.Equals(password) || !validLoad)
                        returnCode = LoginCodesEnumeration.InvalidPassword;

                    character.GetStreamWriter().WriteByte((byte)returnCode);
                    character.GetStreamWriter().WriteByte((int)character.GetDetails().Rights);
                    character.GetStreamWriter().WriteByte(0);
                    character.GetStreamWriter().WriteByte(0);
                    character.GetStreamWriter().WriteByte(0);
                    character.GetStreamWriter().WriteByte(1);
                    character.GetStreamWriter().WriteByte(0);
                    character.GetStreamWriter().WriteByte(character.GetDetails().ConnectionId);
                    character.GetStreamWriter().WriteByte(0);
                    this.loginStreamBuffer.DirectFlushStream(character);

                    if (character.GetLocation().GetLocalX() == -1 && character.GetLocation().GetLocalX() == -1)
                        character.SetLocation(Location.New(3222, 3219, 0));

                    character.GetMovement().GetNextMovement();
                    RuneScape.GetPacketManager().GetFrames().SendMapRegion(character);
                    RuneScape.GetPacketManager().GetFrames().SendLoginInterfaces(character);
                    RuneScape.GetPacketManager().GetFrames().SendWindowPane(character, 548);
                    RuneScape.GetCharacterManager().Register(character);
                    this.loginStreamBuffer.DirectFlushStream(character);

                    Jolt.GetLog().WriteDebug(character.GetDetails().Username);

                    character.GetStatus().Online = true;
                    character.GetStatus().LoginStage = -1;
                }

            }
            catch (Login.Exceptions.InvalidLoginException ile)
            {
                throw ile;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        /// <summary>
        /// Add a character to the login queue.
        /// </summary>
        /// <param name="character">The player to queue in for login.</param>
        public void AddToLoginQueue(Character character)
        {
            queuedCharacters.Add(character);
        }

        /// <summary>
        /// Remove a player from the login queue.
        /// </summary>
        /// <param name="character">The player to queue out from the login.</param>
        public void RemoveFromLoginQueue(Character character)
        {
            queuedCharacters.Remove(character);
        }

        /// <summary>
        /// Accessor for the queued players hashset.
        /// </summary>
        /// <returns></returns>
        public HashSet<Character> GetQueuedCharacters()
        {
            return this.queuedCharacters;
        }

        /// <summary>
        /// Accessor for the update server class.
        /// </summary>
        /// <returns></returns>
        public LoginUpdateServer GetUpdateServer()
        {
            return this.loginUpdateServer;
        }

        /// <summary>
        /// Accessor for the stream buffer class.
        /// </summary>
        /// <returns></returns>
        public LoginStreamBuffer GetStreamBuffer()
        {
            return this.loginStreamBuffer;
        }
        #endregion
    }
}
